package model;

import java.io.*;
import java.util.*;

import algorithm.sphericharmonics.VoxelizedSphereManager;

import viewer.JRoff;


public class GeometryModel3D {

    public RoffVector3D[] vertices;
    public RoffPolygon3D[] polygons;
    public RoffPolygonEdge[] edges;

    public int numVertices;
    public int numPolygons;
    public int numEdges;
    public int numUniqueEdges;
    public int largestPolygonSize;
    
	double x_center=0; 
	double y_center=0; 
	double z_center=0; 
	boolean isCentroidComputed=false; 
	


    public double maxX;
    public double minX;
    public double maxY;
    public double minY;
    public double maxZ;
    public double minZ;
    
    public double scale; 

    private int maxVertsUniqueEdges;
    public boolean validUniqueCount;
    public long modelID; 

    public GeometryModel3D() {
        vertices = null;
        polygons  = null;
        edges     = null;
        numVertices         = 0;
        numPolygons         = 0;
        numEdges            = 0;
        numUniqueEdges      = 0;
        largestPolygonSize  = 0;
        maxVertsUniqueEdges = 10000;
        validUniqueCount    = true;
    }
	private void doCalculateCentroid(){
		RoffTriangleManager tmanager=new RoffTriangleManager();
		tmanager.setPointArray(this.vertices); 
		tmanager.generateTrangles(this);
		int size=tmanager.getNumberOfTriangles();
		double area=0;
		x_center=0; 
		y_center=0; 
		z_center=0; 
		for (int i=0; i<size; i++){
			RoffTriangle tri=tmanager.get(i);
			double triangleArea=tri.getArea(); 
			RoffVector3D c=tri.getCentroid(); 
			x_center+=triangleArea*c.x;
			y_center+=triangleArea*c.y;
			z_center+=triangleArea*c.z;
			area+=triangleArea; 
		}
		x_center=x_center/area; 
		y_center=y_center/area; 
		z_center=z_center/area; 
	}
    public RoffVector3D getCenteroid(){
    	if (!this.isCentroidComputed){
    		this.doCalculateCentroid();
    		this.isCentroidComputed=true; 
    	}
    	/*
    	for (int i=0; i<vertices.length;i++){
    		x_center+=vertices[i].x; 
    		y_center+=vertices[i].y;
    		z_center+=vertices[i].z;
    	}
    	x_center=x_center/vertices.length;
    	y_center=y_center/vertices.length; 
    	z_center=z_center/vertices.length;
    	*/
    	return new RoffVector3D(x_center,y_center,z_center); 
    }
    public RoffVector3D getPoint(int index) { 
        int vertIndex = edges[index / 2].startIndex;
        if (index % 2 != 0) {
            vertIndex = edges[index / 2].endIndex;
        }
        return vertices[vertIndex];
    }
    public int getPointIndex(int index) {
        int vertIndex = edges[index / 2].startIndex;

        if (index % 2 != 0) {
            vertIndex = edges[index / 2].endIndex;
        }

        return vertIndex;
    }
    public void readInfo(BufferedReader info){
    	try{
    		Properties p=new Properties();
    		p.load(info);
    		this.scale=Double.parseDouble(p.getProperty("scale"));
    		this.modelID=Long.parseLong(p.getProperty("mid"));
    		/*
    		String center=p.getProperty("center");
    		center=center.substring(1);
    		center=center.substring(0,center.length()-1);
    		String[] splited=center.split(","); 
    		System.out.println(splited[0]+","+splited[1]+","+splited[2]);
    		x_center=Double.parseDouble(splited[0])/scale;
    		y_center=Double.parseDouble(splited[1])/scale;
    		z_center=Double.parseDouble(splited[2])/scale;
    		*/
    		//this.isCentroidComputed=true; 
    		//System.out.println("modelID:"+modelID); 
    	}catch (Exception ex){
    		ex.printStackTrace(); 
    	}
    }
    
    public static void main(String[] args){
    	try {
    		String off="/home/cyliu/mobileworkspace/javamesh/benchmark/db/17/m1785/m1785.off";
    		String info=JRoff.makeInfoName(off); 
    		BufferedReader in = new BufferedReader(new FileReader(info));
    		GeometryModel3D m=new GeometryModel3D(); 
    		m.readInfo(in);  
    	}catch (Exception ex){
    		ex.printStackTrace(); 
    	}
    }
    
    
    private double parseDouble(String s){
    	return Double.parseDouble(s)/scale; 
    }
    public boolean read(BufferedReader in,BufferedReader info) {
        boolean rval = true;
        this.isCentroidComputed=false; 
        try {
        	readInfo(info); 
            // Temporary variables to load into, so if
            // the load fails we haven't corrupted the
            // current data.
            int inPoints = 0;
            int inPolygons = 0;
            int inTotalEdges = 0;
            int inLargestPolygonSize = 0;
            double inMinX = 0;
            double inMaxX = 0;
            double inMinY = 0;
            double inMaxY = 0;
            double inMinZ = 0;
            double inMaxZ = 0;
            RoffVector3D[] invertices = null;
            RoffPolygon3D[] inPolys = null;
            String readString;
            StringTokenizer st;

            readString = in.readLine().trim(); // Remove the 'OFF'
            if (!readString.equals("OFF")) {
                rval = false;
            }

            readString = in.readLine();
            st = new StringTokenizer(readString);
            
            inPoints   = Integer.parseInt(st.nextToken());
            inPolygons = Integer.parseInt(st.nextToken());

            // The number of edges in the file is still
            // in readString, but we will calculate the
            // edges from the polygons, instead of using
            // this value.
		
            if ((rval) &&
                (inPoints > 0) &&
                (inPolygons > 0)
                ) {
                invertices = new RoffVector3D[inPoints];
                inPolys     = new RoffPolygon3D[inPolygons];
            } else {
                rval = false;
            }

            if (rval) {
                st = new StringTokenizer(in.readLine());

                invertices[0] = new RoffVector3D();
                invertices[0].x = parseDouble(st.nextToken());
                inMaxX = invertices[0].x;
                inMinX = invertices[0].x;

                invertices[0].y = parseDouble(st.nextToken());
                inMaxY = invertices[0].y;
                inMinY = invertices[0].y;

                invertices[0].z = parseDouble(st.nextToken());
                inMaxZ = invertices[0].z;
                inMinZ = invertices[0].z;
			
                for (int i = 1;i < inPoints;i++) {
                    st = new StringTokenizer(in.readLine());

                    invertices[i] = new RoffVector3D();
                    invertices[i].x = parseDouble(st.nextToken());
                    if (invertices[i].x > inMaxX) {
                        inMaxX = invertices[i].x;
                    } else if (invertices[i].x < inMinX) {
                        inMinX = invertices[i].x;
                    }

                    invertices[i].y = parseDouble(st.nextToken());
                    if (invertices[i].y > inMaxY) {
                        inMaxY = invertices[i].y;
                    } else if (invertices[i].y < inMinY) {
                        inMinY = invertices[i].y;
                    }

                    invertices[i].z = parseDouble(st.nextToken());
                    if (invertices[i].z > inMaxZ) {
                        inMaxZ = invertices[i].z;
                    } else if (invertices[i].z < inMinZ) {
                        inMinZ = invertices[i].z;
                    }
                }
            }

            // If rval is still true, then all of the points have been read in.
            if (rval) {
                int polygonSize = 0;
                for (int i = 0;i < inPolygons;i++) {
                    readString = in.readLine();
                    readString = readString.trim();
                    while (readString.length() < 1) {
                        readString = in.readLine();
                        readString = readString.trim();
                    }
                    inPolys[i] = new RoffPolygon3D(readString);
                    polygonSize = inPolys[i].getNumEdges();
                    inTotalEdges = inTotalEdges + polygonSize;
                    if (polygonSize > inLargestPolygonSize) {
                        inLargestPolygonSize = polygonSize;
                    }
                }
            }

            if (rval) {
                numVertices = inPoints;
                numPolygons = inPolygons;
                numEdges = inTotalEdges;
                largestPolygonSize = inLargestPolygonSize;

                vertices = invertices;
                polygons = inPolys;
                minX = inMinX;
                maxX = inMaxX;
                minY = inMinY;
                maxY = inMaxY;
                minZ = inMinZ;
                maxZ = inMaxZ;
                BuildEdges();
            }
        } catch (Exception e) {
            // e.printStackTrace();
            vertices = null;
            polygons  = null; 
            edges     = null;

            numVertices       = 0;
            numPolygons        = 0;
            numEdges           = 0;
            numUniqueEdges     = 0;
            largestPolygonSize = 0;

            maxX = 0;
            minX = 0;
            maxY = 0;
            minY = 0;
            maxZ = 0;
            minZ = 0;

            rval = false;       
        }

        return rval;
    }

    public boolean write(PrintWriter out) {

        boolean rval = true;

        try {
            out.println("OFF");
            out.println(numVertices + " " +
                        numPolygons + " " +
                        numEdges);
            for (int i = 0; i < numVertices; i++) {
                out.println(vertices[i].x + " " + vertices[i].y + " " + vertices[i].z);
            }

            for (int i = 0;i < numPolygons;i++) {
                out.println(polygons[i].toString());
            }
        } catch (Exception e) {
            rval = false;
        }
        return rval;
    }

    private void BuildEdges() {

	// Initialize the number of unique edges found
	// to zero.
	numUniqueEdges = 0;

	RoffPolygonEdge[] tempEdges = new RoffPolygonEdge[numEdges];

	// Two variables for moving through the edges in the polygons.
	int currentEdges = 0;
	RoffPolygonEdge testEdge;
        // Two variables for the edge lookup matrix if needed.
        int mSize = 0;
        boolean edgeMatrix[][] = null;

        // If we have a huge number of vertices, don't even bother.
        if (numVertices > maxVertsUniqueEdges) {
            validUniqueCount = false;
        } else {
            validUniqueCount = true;
            // Declare and initialize a matrix for keeping track of
            // edges.
            mSize = numVertices - 1;
            edgeMatrix = new boolean[mSize][];
            for (int i = 0; i < mSize; i++) {
                edgeMatrix[i] = new boolean[mSize - i];
            }
        }

        for (int i = 0;i < numPolygons;i++) {
            // For every edge in the polygon
	    currentEdges = polygons[i].getNumEdges();
	    for (int j = 0; j < currentEdges; j++) {
                // Retrieve the edge.
                testEdge = polygons[i].getEdge(j);
	        // Check if we already have the edge, if we're checking.
                if (validUniqueCount) {
                    // If it's a real edge and we don't have it, add it.
                    if ((testEdge.startIndex != testEdge.endIndex) &&
                        !edgeMatrix[testEdge.startIndex][mSize - testEdge.endIndex]) {
                        edgeMatrix[testEdge.startIndex][mSize - testEdge.endIndex] = true;
                        tempEdges[numUniqueEdges] = testEdge;
                        numUniqueEdges++;
                    }
                } else {
                    // If we aren't checking, just add it and count it.
                    tempEdges[numUniqueEdges] = testEdge;
                    numUniqueEdges++;
                }
            }
        }

        edges = new RoffPolygonEdge[numUniqueEdges];
        for (int i = 0; i < numUniqueEdges;i++) {
            edges[i] = tempEdges[i];
        }
        tempEdges = null;
    }
}